package com.slofzx.nuoda.evaluation.project.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.slofzx.nuoda.common.enums.CommonResultEnum;
import com.slofzx.nuoda.common.enums.ServiceResult;
import com.slofzx.nuoda.common.exception.ServiceException;
import com.slofzx.nuoda.common.feign.FileInfoClient;
import com.slofzx.nuoda.common.service.impl.BaseServiceImpl;
import com.slofzx.nuoda.common.vo.SysUserVO;
import com.slofzx.nuoda.custom.template.service.TemplateService;
import com.slofzx.nuoda.evaluation.param.service.ProjectCostDataService;
import com.slofzx.nuoda.evaluation.project.enums.ProductTypeEnum;
import com.slofzx.nuoda.evaluation.core.util.StringUtil;
import com.slofzx.nuoda.evaluation.feign.ComputeInvokeClient;
import com.slofzx.nuoda.evaluation.project.compont.exported.BatchExportExcelComponent;
import com.slofzx.nuoda.evaluation.project.compont.exported.ExportExcelContext;
import com.slofzx.nuoda.evaluation.project.compont.imported.BatchImportExcelComponent;
import com.slofzx.nuoda.evaluation.project.compont.imported.ImportExcelContext;
import com.slofzx.nuoda.evaluation.project.dao.BatchInfoDAO;
import com.slofzx.nuoda.evaluation.project.dto.*;
import com.slofzx.nuoda.evaluation.project.entity.BatchInfo;
import com.slofzx.nuoda.evaluation.project.enums.*;
import com.slofzx.nuoda.evaluation.project.observer.BatchObserver;
import com.slofzx.nuoda.evaluation.project.service.*;
import com.slofzx.nuoda.evaluation.project.util.BusiUtil;
import com.slofzx.nuoda.evaluation.project.util.DoubleUtil;
import com.slofzx.nuoda.evaluation.project.vo.*;
import com.slofzx.nuoda.evaluation.steretype.ExcelExportItemLoader;
import com.slofzx.nuoda.excel.entity.TemplateConfig;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * 评价任务基本信息表服务实现类
 *
 * @author nuoda
 * @since 2023-10-20 09:08:53
 */
@Service("batchInfoService")
public class BatchInfoServiceImpl extends BaseServiceImpl implements BatchInfoService {

    @Resource
    private BatchInfoDAO batchInfoDAO;
    @Autowired
    private FileInfoClient fileInfoClient;
    @Autowired
    private BatchCommonDataService batchCommonDataService;
    @Autowired
    private ProjectInfoService projectInfoService;
    @Autowired
    private ProjectYearDataService projectYearDataService;
    @Autowired
    private ProjectReportDetailYearService projectReportDetailYearService;
    @Autowired(required = false)
    protected Set<BatchObserver> batchObservers;
    @Autowired
    private BatchImportExcelComponent importExcelComponent;
    @Autowired
    private BatchExportExcelComponent exportExcelComponent;
    @Autowired
    private TemplateService templateService;
    @Autowired
    private ComputeInvokeClient computeInvokeClient;
    @Autowired
    private ExportTemplateDetailService exportTemplateDetailService;
    @Autowired
    private ExcelExportItemLoader excelExportItemLoader;
    @Autowired
    private ProjectCostDataService projectCostDataService;

    /**
     * 将数据表数据转换为返回数据类型
     *
     * @param entity 数据表对应类
     * @return 实例对象
     */
    protected BatchInfoVO parseEntity2VO(BatchInfo entity) {
        BatchInfoVO vo = new BatchInfoVO();
        vo.setId(entity.getId());
        vo.setYear(entity.getYear());
        vo.setName(entity.getName());
        vo.setParamVersion(entity.getParamVersion());
        vo.setModelId(entity.getModelId());
        vo.setState(entity.getState());
        vo.setTotalInvest(entity.getTotalInvest());
        vo.setProjectTotalCount(entity.getProjectTotalCount());
        vo.setProjectPassCount(entity.getProjectPassCount());
        vo.setCostEvaMethod(entity.getCostEvaMethod());
        vo.setCostEvaMethodBool(entity.getCostEvaMethod());
        vo.setCreateUser(entity.getCreateUser());
        vo.setCreateTime(entity.getCreateTime());
        vo.setType(entity.getType());
        vo.setProductType(entity.getProductType());
        vo.setQuality(entity.getQuality());
        vo.setCutoff(entity.isCutoff());
        vo.setVersionId(entity.getVersionId());
        return vo;
    }

    /**
     * 将数据传递类转换为数据表数据类
     *
     * @param dto 数据传递类实例
     * @return 实例对象
     */
    protected BatchInfo parseDTO2Entity(BatchInfoDTO dto) {
        BatchInfo entity = new BatchInfo();
        entity.setId(dto.getId());
        entity.setName(dto.getName());
        entity.setYear(dto.getYear());
        entity.setParamVersion(dto.getParamVersion());
        entity.setModelId(dto.getModelId());
        entity.setState(dto.getState());
        entity.setTotalInvest(dto.getTotalInvest());
        entity.setProjectTotalCount(dto.getProjectTotalCount());
        entity.setProjectPassCount(dto.getProjectPassCount());
        entity.setCostEvaMethod(dto.getCostEvaMethod());
        entity.setType(dto.getType());
        entity.setProductType(dto.getProductType());
        entity.setQuality(dto.getQuality());
        entity.setCutoff(dto.getCutoff());
        entity.setVersionId(dto.getVersionId());
        return entity;
    }

    /**
     * 通过ID查询单条数据
     *
     * @param id 主键
     * @return 实例对象
     */
    @Override
    public BatchInfoVO queryById(String id) {
        BatchInfo entity = this.batchInfoDAO.selectById(id);
        if (entity == null) {
            return null;
        }
        return parseEntity2VO(entity);
    }

    /**
     * 查询多条数据
     *
     * @param dto 数据传递类实例对象
     * @return 对象列表
     */
    @Override
    public List<BatchInfoVO> queryList(BatchInfoDTO dto) {
        //mybatis-plus 写法示例
        //IPage<BatchInfo> pageResult = this.evaModelDAO.selectPage(new Page<>(dto.getPageNo(),dto.getPageSize()),null);
        //List<BatchInfo> dataList = pageResult.getRecords();
        //dto.setCount((int) pageResult.getTotal());
       // final SysUserVO user = getUser();
//        if (!user.isAdmin()) {
//            final UserQueryDTO userQueryDTO = new UserQueryDTO();
//            this.updateFilterOffice(userQueryDTO, user);
//
//            final List<SysUserVO> userVOS = userClient.query(userQueryDTO).getData();
//            if (CollectionUtils.isNotEmpty(userVOS)) {
//                final Set<String> userIdSet = userVOS.stream().filter(Objects::nonNull).map(SysUserVO::getId).collect(Collectors.toSet());
//                dto.setFilterUserIdSet(userIdSet);
//            } else {
//                final HashSet<String> filterUserIdSet = new HashSet<String>() {{
//                    add(user.getId());
//                }};
//                dto.setFilterUserIdSet(filterUserIdSet);
//            }
//        }
        final SysUserVO user = getUser();
        if (!BusiUtil.checkPermission(user,PermissionEnum.BATCH_QUERY_ALL)) {
            dto.setCreateUser(user.getId());
        }
        List<BatchInfo> dataList = this.batchInfoDAO.queryList(dto);
        if (null == dataList || dataList.isEmpty()) {
            return Collections.emptyList();
        }
        final Set<String> batchIdSet = dataList.stream().map(BatchInfo::getId).collect(Collectors.toSet());
        final ProjectInfoDTO projectInfoDTO = new ProjectInfoDTO();
        projectInfoDTO.setFilterBatchIdSet(batchIdSet);
        final List<ProjectInfoVO> projectInfoVOS = projectInfoService.queryVOList(projectInfoDTO);
        Map<String, List<ProjectInfoVO>> projectMap = null;
        if (CollectionUtils.isNotEmpty(projectInfoVOS)) {
            projectMap = projectInfoVOS.stream().collect(Collectors.groupingBy(ProjectInfoVO::getBatchId));
        }
        List<BatchInfoVO> resultList = new ArrayList(dataList.size());
        for (BatchInfo item : dataList) {
            BatchInfoVO vo = parseEntity2VO(item);

            if (MapUtils.isNotEmpty(projectMap)) {
                final List<ProjectInfoVO> infoVOList = projectMap.get(item.getId());
                if (CollectionUtils.isNotEmpty(infoVOList)) {
                    vo.setProjectTotalCount(infoVOList.size());
                    Double totalInvest = 0D;
                    int passCount = 0;
                    for (ProjectInfoVO projectInfoVO : infoVOList) {
                        totalInvest = DoubleUtil.add(totalInvest, projectInfoVO.getTotalInvest());
                        if (PassTypeEnum.PASS.getId().equals(projectInfoVO.getPassed())) {
                            passCount++;
                        }
                    }
                    vo.setProjectPassCount(passCount);
                    vo.setTotalInvest(DoubleUtil.getScale(totalInvest, 2));
                }
            }
            resultList.add(vo);

        }
        // TODO 添加其他关联查询数据
        return resultList;
    }

    /**
     * 新增数据
     *
     * @param dto 实例对象
     * @return 操作结果
     */
    @Override
    public ServiceResult create(BatchInfoDTO dto) {
        // TODO 添加校验逻辑
        BusiUtil.checkNotNull(dto.getName(), "任务名称不能为空");
        BusiUtil.checkNotNull(dto.getParamVersion(), "公共参数不能为空");
        BusiUtil.checkNotNull(dto.getModelId(), "评价模型不能为空");
        checkDuplicateName(dto.getName(), null);
        BatchInfo entity = parseDTO2Entity(dto);
        entity.setState(BatchStateEnum.INIT.getId());
        entity.setCutoff(false);
        preInsert(entity);
        this.batchInfoDAO.insert(entity);
        return CommonResultEnum.SUCCESS;
    }

    /**
     * 检查任务名称是否重复
     *
     * @param name
     */
    private void checkDuplicateName(String name, String id) {
        LambdaQueryWrapper<BatchInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(BatchInfo::getName, name);
        if (StringUtils.isNotEmpty(id)) {
            queryWrapper.ne(BatchInfo::getId, id);
        }
        final Long count = batchInfoDAO.selectCount(queryWrapper);
        if (count > 0) {
            throw new ServiceException("任务名称重复，请修改后提交", null, null);
        }
    }

    /**
     * 修改数据
     *
     * @param dto 实例对象
     * @return 操作结果
     */
    @Override
    public ServiceResult update(BatchInfoDTO dto) {
        String id = dto.getId();
        if (StringUtils.isEmpty(id)) {
            return CommonResultEnum.NOT_EXIST;
        }
        if (StringUtils.isNotEmpty(dto.getName())) {
            checkDuplicateName(dto.getName(), id);
        }
        BatchInfo entity = this.batchInfoDAO.selectById(id);
        if (entity == null) {
            return CommonResultEnum.NOT_EXIST;
        }
        // TODO 添加其他校验逻辑，如数据权限

        entity.setName(dto.getName());
        entity.setYear(dto.getYear());
        entity.setParamVersion(dto.getParamVersion());
        entity.setModelId(dto.getModelId());
        entity.setState(dto.getState());
        entity.setTotalInvest(dto.getTotalInvest());
        entity.setProjectTotalCount(dto.getProjectTotalCount());
        entity.setProjectPassCount(dto.getProjectPassCount());
        entity.setCostEvaMethod(dto.getCostEvaMethod());
        entity.setType(dto.getType());
        entity.setProductType(dto.getProductType());
        entity.setQuality(dto.getQuality());
        entity.setCutoff(dto.getCutoff());
        entity.setVersionId(dto.getVersionId());
        this.batchInfoDAO.updateById(entity);
        return CommonResultEnum.SUCCESS;
    }

    /**
     * 通过主键删除数据
     *
     * @param id 主键
     * @return 操作结果
     */
    @Override
    @Transactional
    public ServiceResult deleteById(String id) {
        if (StringUtils.isBlank(id)) {
            return CommonResultEnum.VALUE_LOOSE;
        }
        this.batchInfoDAO.deleteById(id);

        if (batchObservers != null) {
            for (BatchObserver item : batchObservers) {
                ServiceResult result = item.handleBatchDelete(id);
                if (CommonResultEnum.SUCCESS != result) {
                    logger.error("项目监听者 {} 处理任务删除事件失败: {}", item.getName(), result);
                }
            }
        }
        return CommonResultEnum.SUCCESS;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ServiceResult importExcel(String fileId, String batchId) {
        if (StringUtils.isBlank(fileId)) {
            return CommonResultEnum.VALUE_LOOSE;
        }

        final BatchInfo batchInfo = batchInfoDAO.selectById(batchId);
        if (batchInfo == null) {
            return CommonResultEnum.NOT_EXIST;
        }
        final ImportExcelContext importExcelContext = new ImportExcelContext();
        importExcelContext.setId(batchId);
        importExcelContext.setBatchQuality(BatchQualityEnum.OUT_PUT);
        importExcelContext.setProductType(BusiUtil.getEnumById(batchInfo.getProductType(), ProductTypeEnum.class));
        importExcelContext.setFileId(fileId);
        importExcelContext.setParamVersionId(batchInfo.getParamVersion());
        ServiceResult serviceResult = importExcelComponent.importExcel(importExcelContext);
        return serviceResult;


    }

    @Override
    @Transactional
    public ServiceResult editCommonData(BatchInfoDTO dto) {
        String id = dto.getId();
        String name = dto.getName();
        String paramVersion = dto.getParamVersion();
        String modelId = dto.getModelId();
        String costEvaMethod = dto.getCostEvaMethod();
        if (StringUtils.isBlank(id) || StringUtils.isBlank(name) || StringUtils.isBlank(paramVersion) ||
                StringUtils.isBlank(modelId) || StringUtils.isBlank(costEvaMethod)) {
            return CommonResultEnum.VALUE_LOOSE;
        }
        if (CostEvaMethodEnum.getEnum(costEvaMethod) == null) {
            return CommonResultEnum.NOT_EXIST;
        }
        BatchInfo batchInfo = batchInfoDAO.selectById(id);

        if (batchInfo == null) {
            return CommonResultEnum.NOT_EXIST;
        }
        if (StringUtils.isNotEmpty(dto.getParamVersion()) && !dto.getParamVersion().equals(batchInfo.getParamVersion())) {
            final ProjectInfoDTO infoDTO = new ProjectInfoDTO();
            infoDTO.setBatchId(batchInfo.getId());
            final List<ProjectInfoVO> infoVOList = projectInfoService.queryList(infoDTO);
           // final Set<String> projectIdList = infoVOList.stream().map(ProjectInfoVO::getId).collect(Collectors.toSet());
            projectCostDataService.updateByParamVersion(dto.getParamVersion() ,infoVOList);
        }
        batchInfo.setName(dto.getName());
        batchInfo.setModelId(dto.getModelId());
        batchInfo.setCostEvaMethod(dto.getCostEvaMethod());
        batchInfo.setCutoff(dto.isCutoff());
        batchInfo.setCostEvaMethod(dto.getCostEvaMethod());
        batchInfo.setParamVersion(dto.getParamVersion());
        batchInfoDAO.updateById(batchInfo);

        List<BatchCommonDataDTO> batchCommonDTOList = dto.getBatchCommonDTOList();
        if (batchCommonDTOList != null && !batchCommonDTOList.isEmpty()) {
            batchCommonDataService.batchInsertData(id, batchCommonDTOList);
        }
        return CommonResultEnum.SUCCESS;
    }




    @Override
    public List<BatchInfoNameVO> queryNameList() {
        final BatchInfoDTO dto = new BatchInfoDTO();
        final List<BatchInfo> batchInfos = batchInfoDAO.queryList(dto);
        if (CollectionUtils.isEmpty(batchInfos)) {
            return Collections.EMPTY_LIST;
        }

        return batchInfos.stream().map(m -> {
            final BatchInfoNameVO batchInfoNameVO = new BatchInfoNameVO();
            batchInfoNameVO.setId(m.getId());
            batchInfoNameVO.setValueKey(m.getId());
            batchInfoNameVO.setName(m.getName());
            return batchInfoNameVO;
        }).collect(Collectors.toList());
    }

    /**
     * 导出项目数据
     * @param projectInfoDTO
     */
    @Override
    public ResponseEntity<byte[]> exportExcel(HttpServletRequest request, ProjectInfoDTO projectInfoDTO) {
        BusiUtil.checkNotNull(projectInfoDTO.getTemplateId(),"请先选择自定义模板");
        BusiUtil.checkNotNull(projectInfoDTO.getBatchId(),"任务ID不允许为空");
        final BatchInfo batchInfo = batchInfoDAO.selectById(projectInfoDTO.getBatchId());
        String excelName = batchInfo.getName() + "导出数据.xlsx";
        TemplateConfig config = templateService.getTemplateConfig(projectInfoDTO.getTemplateId());
        return export(() -> {
            final ExportExcelContext exportExcelContext = new ExportExcelContext();
            exportExcelContext.setId(batchInfo.getId());
            exportExcelContext.setCostEvaMethod(batchInfo.getCostEvaMethod());
            exportExcelContext.setProductType(BusiUtil.getEnumById(batchInfo.getProductType(), ProductTypeEnum.class));
            exportExcelContext.setProjectInfoDTO(projectInfoDTO);
            exportExcelContext.setConfig(config);
            exportExcelContext.setExcelExportItemList(excelExportItemLoader.getDataList());
            return exportExcelComponent.exportExcel(exportExcelContext);
        }, request, excelName);
    }


    /**
     * 根据筛选条件导出合并数据
     * @param request
     * @param batchId
     * @param templateId
     */
    @Override
    public ResponseEntity<byte[]> downloadMergeExcel(HttpServletRequest request, String batchId, String templateId) {
        BusiUtil.checkNotNull(templateId,"请先选择模板");
        BusiUtil.checkNotNull(batchId,"任务ID不允许为空");
        final BatchInfo batchInfo = batchInfoDAO.selectById(batchId);
        final TemplateConfig templateConfig = templateService.getTemplateConfig(templateId);
        String excelName = batchInfo.getName() + "合并导出数据.xlsx" ;
        // 项目数据
        return export(()->{
            final ExportExcelContext context = new ExportExcelContext();
            context.setId(batchId);
            context.setConfig(templateConfig);
            context.setExcelExportItemList(excelExportItemLoader.getDataList());
            return exportExcelComponent.mergeExportExcel(context);
        },request,excelName);

    }

    @Override
    public ResponseEntity<byte[]> batchExportExcel(HttpServletRequest request, ProjectInfoDTO projectInfoDTO) {
        final String batchId = projectInfoDTO.getBatchId();
        final String templateId = projectInfoDTO.getTemplateId();
        BusiUtil.checkNotNull(templateId,"请先选择模板");
        BusiUtil.checkNotNull(batchId,"任务ID不允许为空");
        final BatchInfo batchInfo = batchInfoDAO.selectById(batchId);
        final TemplateConfig templateConfig = templateService.getTemplateConfig(templateId);
        String excelName = batchInfo.getName() + "批量导出数据.xlsx" ;
        return export(() -> {
            final ExportExcelContext context = new ExportExcelContext();
            context.setId(batchId);
            context.setProductType(BusiUtil.getEnumById(batchInfo.getProductType(), ProductTypeEnum.class));
            context.setProjectInfoDTO(projectInfoDTO);
            context.setExcelExportItemList(excelExportItemLoader.getDataList());
            context.setConfig(templateConfig);
            final byte[] bytes = exportExcelComponent.batchExportExcel(context);
            return bytes;
        }, request, excelName);
    }

    /**
     * 导出通用封装
     * @param supplier
     * @param
     */
    public ResponseEntity<byte[]> export(Supplier<byte[]> supplier, HttpServletRequest request, String excelName) {
        final byte[] bytes = supplier.get();
        HttpHeaders headers = new HttpHeaders();
        headers.add("content-disposition", "attachment; filename=" + StringUtil.convertDownloadFileName(excelName,request.getHeader("User-Agent")));
        HttpStatus statusCode = HttpStatus.OK;
        ResponseEntity<byte[]> entity = new ResponseEntity<byte[]>(bytes, headers, statusCode);
        return entity;
    }
}
